13 فصل سیزدهم - تفکر پایتونی

#### فصل 13
#### انتخاب ساختمان داده

تا اینجا ساختمان داده های اصلی پایتون را یاد گرفتید و چندین الگوریتم را که از آنها استفاده کردید را مشاهده کردید. اگر می خواهید بیشتر در مورد الگوریتم ها بدانید، شاید الان فرصت خوبی باشد تا فصل B را بخوانید. اما مجبور نیستید قبل از رفتن به فصل بعد، این فصل را بخوانید، می توانید هر موقع علاقه داشتید این فصل را بخوانید.

این فصل یک مطالعه موردی با تمرین هایش را ارائه می دهد که اجازه می دهد درمورد انتخاب ساختمان داده ها فکر کنید و استفاده از آنها را تمرین کنید.

#### 13.1 تحلیل مسئله فراوانی کلمه

بطور معمول، قبل از اینکه راه حل من را بخوانید حداقل باید یکبار سعی در حل تمرین کنید.

#### تمرین 13.1

برنامه ای بنویسید که یک فایل را بخواند، هر خط را به کلمات بشکند، کاراکتر های فاصله (space) و نشانه گذاری را از کلمات پاک کند و آنها به حروف کوچک تبدیل کند.

نکته: ماژول string رشته ای بنام " whitespace" که شامل فاصله، تب (tab)، خط جدید (newline) و... و رشته ای بنام " punctuation" که شامل کاراکتر های نشانه گذاری می باشد را فراهم می کند. بیایید ببینیم، اگر به پایتون قسم می خوریم:



همچنین می توانید استفاده از متدهای strip، replace و translate را هم در نظر بگیرید.

#### تمرین 13.2

به پروژه Gutenberg (http://gutenberg.org) و کتاب خارج از حق نشر (out-of-copyright) محبوب خود را در فرمت متن خام (plain text) دانلود کنید.

برنامه خود را از تمرین قبلی به خواندن کتابی که دانلود کردید اصلاح کنید، اطلاعات بالای سر صفحه در ابتدای فایل را صرفنظر کنید و مانند تمرین قبل بقیه کلمات را پردازش کنید. اکنون برنامه خود برای شمارش مجموع تعداد کلمات و تعداد استفاده هر کلمه در کتاب تغییر دهید.

تعداد کلمات مختلف استفاده شده در کتاب را چاپ کنید. کتاب های مختلف با نویسنده های متفاوت در دوره های تاریخی متفاوت را مقایسه کنید. کدام نویسنده بیشترین وسعت واژگان را استفاده می کند؟

#### تمرین 13.3

برنامه خود را از تمرین قبل به چاپ 20 پر تکرار ترین کلمه های استفاده شده در کتاب اصلاح کنید.

#### تمرین 13.4

برنامه قبلی را برای خواندن یک لیست کلمات (بخش 9.1 را مشاهده کنید) و سپس چاپ همه کلمات در کتاب که در لیست کلمات نیستند، تغییر دهید. چه تعداد غلط املایی در آنها وجود دارد؟ چه تعداد از آنها کلمات رایج هستند که باید در لیست کلمات بیایند و چه تعداد آنها واقعاً مبهم هستند؟

#### 13.2 اعداد تصادفی

با توجه به ورودی های مشابه، بیشتر برنامه های کامپیوتری خروجی های مشابه در هر زمان تولید می کنند، بنابراین به آنها قطعی یا جبرگرایی گفته می شود. قطعیت معمولا چیز خوبی است، چون انتظار داریم محاسبه مشابه به عملکرد، نتیجه مشابه داشته باشد. هرچند برای برخی از برنامه ها می خواهیم که کامپیوتر غیر قابل پیش بینی باشد. بازی ها یک مثال آشکار هستند، ولی تعداد برنامه های بیشتری وجود دارند.

ساختن یک برنامه واقعاً غیر قطعی مشخصا دشوار است اما راه هایی برای ساخت آن (حداقل به ظاهر غیر قطعی) وجود دارد. یکی از آنها استفاده از الگوریتم هایی است که اعداد شبه تصادفی ایجاد می کنند. اعداد شبه تصادفی واقعاً تصادفی نیستند چون آنها توسط یک محاسبه قطعی تولید می شوند. اما فقط با نگاه کردن به اعداد اینطور به نظر می رسد و تمایز دادن آنها از اعداد تصادفی غیر ممکن است.

ماژول random متد هایی جهت تولید اعداد شبه تصادفی (که از اینجا به بعد برای سادگی "تصادفی" می گویم) فراهم می کند.

متد random یک عدد شناور بین 0.0 و 1.0 برمی گرداند (شامل 0.0 می شود اما 1.0 نه). هر بار که متد random را فراخوانی کنید عدد بعدی را در یک سری طولانی دریافت می کنید. برای دیدن یک مثال، این حلقه را اجرا کنید:



متد randint پارامترهای low و high را می گیرد و یک عدد صحیح بین این دو بر می گرداند (شامل هر دو عدد هم می شود).



برای انتخاب یک عنصر از توالی بصورت تصادفی، می توانید از متد choice استفاده کنید:



ماژول random همچنین متد هایی برای تولید مقادیر تصادفی از توزیع های پیوسته شامل گاوسی، نمایی، گاما و تعدادی دیگر فراهم می کند.

#### تمرین 13.5

یک متد با نام choose_from_hist بنویسید که یک هیستوگرام (که در بخش 11.2 تعریف شد) بگیرد و یک مقدار تصادفی از هیستوگرام را برگرداند. انتخاب با احتمال تناسب نسبت به تکرار باشد. برای مثال، برای این هیستوگرام:



متد شما باید مقدار ‘a’ را با احتمال 3/2 و مقدار ‘b’ را با احتمال 3/1 برگرداند.

#### 13.3 هیستوگرام کلمه

شما قبل از آمدن به این قسمت باید سعی کنید تمرین قبلی را حل کنید. می توانید راه حل های من را از آدرس http://thinkpython2.com/code/analyze_book1.py دانلود کنید. همچنین به http://thinkpython2.com/code/emma.txt هم نیاز خواهید داشت.

این یک برنامه است که یک فایل را می خواند و یک هیستوگرام از کلمات در فایل می سازد:



این برنامه فایل emma.txt (که شامل متن کتاب Emma نوشته Jane Austen) را می خواند. متد process_file میان خط های فایل حلقه می زند (پیمایش می کند) و هر بار یکی از آنها را به process_line پاس می دهد. هیستوگرام بنام hist بعنوان یک انباره استفاده می شود. process_line از متد replace کلاس string برای جایگزین کردن خط تیره ها با کاراکتر فاصله قبل از متد split (برای شکستن هر خط در یک لیست از رشته ها) استفاده می کند. این متد لیست کلمات را پیمایش می کند از متد strip و lower برای حذف نشانه گذاری و تبدیل به حروف کوچک استفاده می کند. (مختصر بگویم که رشته ها تبدیل شدند. به یاد داشته باشید که رشته تغییر ناپذیر هستند؛ بنابراین متد هایی مثل strip و lower یک رشته جدید برمی گردانند.)

در نهایت، متد process_line هیستوگرام را توسط ساختن یک مورد جدید یا افزودن به یک مورد موجود بروز رسانی می کند.

برای شمارش مجموع تعداد کلمات در فایل می توانیم فراوانی در هیستوگرام رو اضافه کنیم:



تعداد کلمات مختلف دقیقا تعداد موارد در دیکشنری است:



این تکه کدی برای چاپ نتیجه است:



و نتیجه:



#### 13.4 بیشترین کلمات رایج

برای پیدا کردن بیشترین کلمات رایج می توانیم یک لیست از tuple ها بسازیم که هر tuple شامل یک کلمه و فراوانی آن باشد، سپس آن را مرتب کنیم.

متد زیر یک هیستوگرام از ورودی می گیرد و یک لیست از tuple شامل کلمه و تکرار آن برمی گرداند:



در هر tuple، اول فراوانی می آید؛ پس لیست نتیجه بر اساس فراوانی مرتب می شود. این یک حلقه است که بیشترین کلمات رایج را چاپ می کند:



من از آرگومان کلیدی sep استفاده کردم برای اینکه به متد بگویم از کاراکتر tab بعنوان یک جدا کننده بجای یک کاراکتر فاصله استفاده کن؛ بنابراین ستون دون مرتب شده است. این نتیجه های کتاب Emma هستند:



این کد با استفاده پارامتر کلیدی متد sort می تواند ساده تر شود. اگر کنجکاو هستید، می توانید درباره آن در آدرس https://wiki.python.org/moin/HowTo/Sorting بخوانید.

#### 13.5 پارامترهای اختیاری

متدهای توکار و متد هایی که آرگومان های اختیاری را دیدیم. همچین نوشتن متد هایی با آرگومان های اختیاری توسط برنامه نویس هم امکان دارد. برای مثال، این یک متد است که بیشترین کلمات رایج را در یک هیستوگرام چاپ می کند



پارامتر اول اجباری است، دومی اختیاری است. مقدار پیش فرض پارامتر num برابر 10 است. اگر فقط می خواهید یک آرگومان ارائه دهید:





پارامتر num مقدار پیش فرض می گیرد. اگر می خواهید دو آرگومان ارائه دهید:

پارامتر num مقدار آرگومان را بجای مقدار پیش فرض می گیرد. به عبارت دیگر، آرگومان اختیاری مقدار پیش فرض را باطل می کند.

اگر یک متد هم پارامتر اختیاری داشته باشد و هم اجباری، همه پارامترهای اجباری باید اول بیایند، به دنبالشان اختیاری ها میایند.

#### 13.6 کاهش Dictionary

پیدا کردن کلماتی از کتاب که در لیست کلمات words.txt نیستند یک مسئله است (ممکن است بعنوان تفریق مجموعه تشخیص دهید) که می خواهیم همه کلمات از مجموعه اول (کلمات در کتاب) که در مجموعه دیگر (لیست کلمات) نیستند را پیدا کنیم.

متد subtract دیکشنری های d1 و d2 را می گیرد و یک دیکشنری که شامل همه کلیدهای d1 که در d2 نیستند را برمی گرداند. از آنجا که واقعاً به مقادیر متناظر کلید ها (value ها در دیکشنری) اهمیتی نمی دهیم همه آنها را با None مقدار می دهیم.



برای پیدا کردن کلماتی در کتاب که در لیست words.txt نیستند می توانیم از متد process_file برای ساخت یک هیستوگرام برای words.txt و سپس از متد subtract استفاده کنیم:



این برخی از نتیجه کتاب Emma است:



برخی از این کلمات نام و مضاف الیه هستند. بقیه – مثل کلمه "مقابله کردن" – کلمه رایج نیستند. اما تعدادی کلمات رایج هستند که باید واقعاً در این لیست باید باشند!

#### تمرین 13.6

پایتون یک ساختمان داده بنام set فراهم می کند که مجموعه ای از عملیات رایج را ارائه می دهد. می توانید درباره آن در بخش 19.5 یا مستندات آدرس http://docs.python.org/3/library/stdtypes.html#types-set را بخوانید.

یک برنامه بنویسید که از set subtraction برای پیدا کردن کلماتی که در کتاب هستند و در لیست نیستند، استفاده کند.

راه حل: http://thinkpython2.com/code/analyze_book2.py

#### 13.7 کلمات تصادفی

برای انتخاب یک کلمه تصادفی از هیستوگرام ساده ترین الگوریتم برای ساخت یک لیست به همراه چندین انتخاب از کلمه طبق تعداد مشاهده و سپس انتخاب از لیست:



عبارت "[word] * freq" یک لیست از کپی های freq از رشته word می سازد. متد extend شبیه متد append است بجز اینکه آرگومان آن یک توالی است.

این الگوریتم کار می کند اما خیلی کارآمد نیست؛ هر وقت یک کلمه تصادفی انتخاب کنید، این متد لیست را دوباره می سازد که این به بزرگی کتاب اصلی است. یک بهبود آشکار این است که اول لیست را بسازید سپس انتخاب های چند گانه را. اما هنوز هم بزرگ است.

یک راه جایگزین این است:

1. از کلید ها برای بدست آوردن لیست کلمات در کتاب استفاده کنید.

2. یک لیست بسازید که شامل جمع تعداد کلمات را یکجا داشته باشد (تمرین 10.2 را ببینید). آخرین مورد در این لیست مجموع تعداد کلمات در این کتاب است (n).

3. یک شماره تصادفی از 1 تا n انتخاب کنید. از یک جستجوی دو بخشی (تمرین 10.10 را ببینید) برای پیدا کردن اندیس که شماره تصادفی می خواهد در مجموع تجمعی درج کند، استفاده کنید.

4. از اندیس برای پیدا کردن کلمه مربوط در لیست کلمات استفاده کنید.

#### تمرین 13.7

یک برنامه بنویسید که از این الگوریتم برای انتخاب یک کلمه تصادفی از کتاب استفاده کند.

راه حل: http://thinkpython2.com/code/analyze_book3.Py

#### 13.8 تحلیل مارکوف

اگر کلماتی بصورت تصادفی از کتاب انتخاب می کنید می توانید لغات با معنی انتخاب کنید اما ممکن است نخواهید یک جمله دریافت کنید:



یک سری از کلمات تصادفیِ بسیار کم، منطقی هستند اما رابطه ای بین کلمات پیاپی وجود ندارد. برای مثال در یک جمله واقعی می توانید انتظار داشته باشید یک حرف تعریف مثل "the" به دنبال یک صفت یا اسم بیاید و ممکن است با فعل یا عبارت قیدی نیاید.

یک راه برای اندازه گیری این نوع از رابطه ها تحلیل مارکوف (Markov) است که برای یک توالی از کلمات، احتمال کلماتی که در بعد این بیایند را تشخیص می دهد. برای مثال، آهنگ Eric با "Half a Bee begins" شروع می شود:



در این متن اصطلاح "half the" همیشه به دنبال کلمه "bee" می آید اما اصطلاح "the bee" ممکن است به دنبال کلمات "has" یا "is" بیاید.

نتیجه تحلیل مارکوف یک نگاشت از هر پیشوند (مانند "half the" و "the bee") برای تمام پسوندهای ممکن (مانند "has" و "is") است.

با توجه به این نگاشت می توانید یک متن تصادفی تولید کنید که با هر پیشوندی شروع شود و از پسوندهای ممکن بصورت تصادفی انتخاب کنید. سپس می توانید پایان پیشوند و پسوند جدید را برای تشکیل پیشوند بعدی ترکیب کنید و این کار را تکرار کنید.

برای مثال اگر با پیشوند "Half a" شروع کردید، پس کلمه بعدی باید "bee" باشد؛ چون این پیشوند فقط یک بار در متن ظاهر شده است. پیشوند بعدی "a bee" است؛ بنابراین پسوند بعدی ممکنه کلمات "philosophically"، "be" یا "due" باشد.

در این مثال اندازه پیشوند همیشه دو عدد است اما می توانید تحلیل مارکوف را با هر اندازه پیشوند انجام دهید.

#### تمرین 13.8 تحلیل مارکوف

1. یک برنامه بنویسید که یک متن از یک فایل بخواند و تحلیل مارکوف را انجام دهد. نتیجه باید یک دیکشنری باشد که پیشوندها را به مجموعه از پسوندهای ممکن نگاشت کند. مجموعه ممکن از نوع list، tuple یا dictionary باشد. این به شما بستگی دارد که یک انتخاب مناسب انجام دهید. می توانید برنامه خود را با اندازه پیشوند دو عدد تست کنید اما باید این برنامه را طوری بنویسید که امتحان کردن بقیه اندازه ها نیز آسان و مقدور باشد.

2. یک متد به برنامه قبلی برای تولید متن تصادفی مبتنی بر تحلیل مارکوف اضافه کنید. این یک مثال از کتاب Emma با اندازه پیشوند دو عدد است:



برای این مثال نشانه گذاری های ضمیمه شده به کلمات را حذف کردم. نتیجه تقریبا از روی قواعد نحوی درست است اما نه کاملا. از نظر معنایی هم تقریبا قابل قبول هستند اما نه کاملا.

چه اتفاقی می افتد اگر اندازه پیشوند را افزایش دهیم؟ آیا متن تصادفی بیشتر منطقی می شود؟

3. یکبار دیگر برنامه تان کار می کند؛ ممکن است بخواهید یک ترکیب دیگر را امتحان کنید. اگر متن را از دو یا چند کتاب ترکیب کنید متن تصادفی که تولید کرده اید مخلوطی از لغات و اصطلاحات از منابعی در روش های جالب خواهد شد.

فرجه: این مورد مطالعه بر پایه یک مثال از کتاب زیر است:

Kernighan and Pike, The Practice of Programming, Addison-Wesley, 1999.

شما باید قبل از ادامه دادن کتاب سعی در حل این تمرین کنید؛ پس می توانید راه حل من را از آدرس http://thinkpython2.com/code/markov.py دانلود کنید. همچنین به http://thinkpython2.com/code/emma.txt نیاز خواهید داشت.

#### 13.9 ساختمان داده ها

استفاده از تحلیل مارکوف برای تولید متن تصادفی جذاب است، اما یک نکته دیگر نیز برای این تمرین وجود دارد: انتخاب ساختمان داده. در راه حل تمرین قبلی باید انتخاب می کردید:

· چگونه پیشوندها را ارائه دهید

· چگونه مجموعی از پسوندهای ممکن را ارائه دهید

· چگونه نگاشتی از هر پیشوند برای مجموعی از پسوندهای ممکن را ارائه دهید

آخرین آسان است: دیکشنری یک انتخاب آشکار است برای یک نگاشت از کلید ها با مقادیر مترادف.

برای پیشوندها بیشترین انتخاب آشکار رشته ها، لیست رشته ها و چندتایی (tuple) از رشته ها هستند.

برای پسوندها یک انتخاب لیست است، دیگری هیستوگرام (دیکشنری) است.

چگونه باید انتخاب کنید؟ اولین قدم فکر کردن درباره عملیاتی است که می خواهید برای هر ساختمان داده پیاده سازی کنید. برای پسوندها، باید قادر باشیم تا کلمات را از اول بر داریم و در آخر اضافه کنیم. برای مثال اگر پیشوند فعلی "half a" باشد و کلمه بعدی "bee"، باید قادر باشید پیشوند بعدی را "a bee" تشخیص دهید.

شاید اول یک لیست انتخاب کنید، چون حذف و اضافه کردن عناصر در آن آسان است. همچنین باید قادر باشیم از پیشوندها بعنوان کلید در دیکشنری استفاده کنیم؛ پس قوانین، لیست ها را رد می کند. با چندتایی ها نمی توانید عنصر حذف و اضافه کنید اما می توانید از عملیات اضافه برای یک چندتایی استفاده کنید:



متد shift یک چندتایی از کلمات (پارامتر prefix) می گیرد و یک رشته (پارامتر word) و یک چندتایی جدید شکل می دهد که شامل همه کلماتی که در پیشوند هستند (بجز اولین) است سپس word را به انتهای آن اضافه می کند.

برای مجموع پسوندها، عملیاتی که نیاز به انجام داریم شامل افزودن یک پسوند (یا افزایش فراوانی یک عنصر موجود) و انتخاب یک پسوند تصادفی هستند.

افزودن یک پسوند به همان اندازه برای پیاده سازی در لیست یا هیستوگرام آسان است. انتخاب یک عنصر تصادفی از لیست ساده است. انتخاب از یک هیستوگرام برای انجام کارایی سخت تر است (تمرین 13.7 را ببینید).

تاکنون بیشتر درباره سهولت پیاده سازی صحبت کردیم، اما فاکتورهای دیگری برای بررسی در انتخاب ساختمان داده وجود دارند. یکی از آنها زمان اجرا است. گاهی اوقات یک دلیل تئوری وجود دارد که انتظار می رود یک ساختمان داده از بقیه سریعتر باشد؛ برای مثال اشاره کردم که عملگر in برای دیکشنری ها سریعتر از لیست ها است (حداقل برای مواقعی که تعداد عناصر زیاد هستند).

اما اغلب نمی دانید که کدام پیاده سازی زودتر از زمان مقرر انجام خواهد شد. یکی از انتخاب ها پیاده سازی هردوتای آنهاست و ببینیم کدام بهتر است. این رویکرد، محک زنی (بنچ مارک) نام دارد. یک راه حل جایگزین کاربردی این است که یک ساختمان داده با آسانترین پیاده سازی را انتخاب کنید؛ سپس ببینید به اندازه کافی برای برنامه مذکور سریع است یا نه. اگر اینطور بود احتیاجی نیست ادامه بدهید، در غیر اینصورت ابزارهایی مانند ماژول پروفایل (profiler) وجود دارند که می توانند جایی که بیشترین زمان در برنامه می گیرد را تشخیص دهند.

فاکتور دیگر برای بررسی فضای ذخیره سازی است. برای مثال استفاده از هیستوگرام برای مجموع پیشوندها ممکن است فضای کمتری بگیرد؛ چون فقط باید هر کلمه را یکبار ذخیره کنید سوای از اینکه چند بار در متن آماده است. همچنین در برخی موارد صرفه جویی در فضای ذخیره سازی برنامه تان را سریعتر اجرا می کند و درنهایت، اگر برنامه تان را خارج از فضای حافظه اجرا کنید، ممکن است برنامه شما ابدا اجرا نشود. اما در بیشتر برنامه های کاربردی فضا یک توجه ثانویه بعد از زمان اجراست.

یک نظر نهایی: در این بحث اشاره کردم که باید از یک ساختمان داده هم برای تحلیل و هم تولید استفاده کنیم. اما چون فازهای جداگانه وجود دارد ممکن است از یک ساختمان داده برای تحلیل استفاده کنید سپس آن را به ساختار دیگری برای تولید تبدیل کنید. این امر می تواند یک پیروزی خالص باشد اگر زمان ذخیره شده طی تولید از زمان صرف شده برای تبدیل تخطی کند.

#### 13.10 اشکال زدایی

5 کار برای سعی کردن وجود دارند که وقتی در حال اشکال زدایی (debugging) یک برنامه هستید و بطور خاص روی یک اشکال سخت کار می کنید:

· خواندن: کدتان را امتحان کنید، برگردید و دوباره آنرا بخوانید و آنرا چک کنید که می گوید: "چه چیز میخواهی بگویی؟!"

· اجرا کردن: توسط تغییرات و اجرا کردن در نسخه های متفاوت آزمایش کنید. غالبا اگر یک چیز درست را در مکان صحیح در برنامه مشاهده کردید، مسئله آشکار می شود اما گاهی اوقات مجبورید چوب بست (scaffolding) بسازید.

· اندیشیدن: وقتی را برای فکر کردن بگذارید! این چه نوع خطایی است؟ نحوی، زمان اجرا یا معنایی؟ چه اطلاعاتی می توانید از متن پیام یا خروجی برنامه بدست آورید؟ چه نوع خطایی می تواند باعث مشکلی باشد که دیدید؟ آخرین تغییر قبل از ظاهر شدن مشکل چه بود؟

· اردک پلاستیکی: اگر مشکل تان را برای دیگری توضیح دهید، گاهی اوقات قبل از اینکه سوال پرسیدن را تمام کنید، جواب را پیدا می کنید. اغلب نیازی نیست از شخص دیگری بپرسید، می توانید برای یک اردک پلاستیکی صحبت کنید و این منبع استراتژی معروف، اشکال زدایی اردک پلاستیکی نام دارد. من این را درست نکردم، آدرس https://en.wikipedia.org/wiki/Rubber_duck_debugging را ببینید.

· عقب نشینی: در برخی موارد بهترین کاری که می شود انجام داد برگرداندن است، تغییرات اخیر را تا زمانی که برنامه کار می کرد و می دانستید به عقب برگردانید. سپس می توانید بازسازی را شروع کنید.

برنامه نویس های تازه کار گاهی اوقات روی یکی از این فعالیت ها گیر کرده و بقیه را فراموش می کنند. هر فعالیت حالت شکست خود را دارد.

برای مثال اگر مشکل یک خطای تایپی باشد، خواندن کدتان ممکن است کمک تان کند اما در سوء تفاهم مفهومی اینطور نیست. اگر نمی دانید برنامه تان چکار انجام می دهد می توانید آنرا 100بار بخوانید و هرگز خطا را نبینید چون خطا در سر شماست!

اجرا کردن آزمایشات می تواند کمک کند، بطور خاص اگر قطعه برنامه کوچک و با تست های ساده اجرا کنید. اما اگر آزمایشات را بدون فکر کردن و خواندن کد اجرا کنید ممکن است توی الگوی که آنرا "برنامه نویسی گام تصادفی" می نامم بیفتید که عبارتند از فرایند ایجاد تغییرات تصادفی تا زمانی که برنامه کار درست را انجام دهد. نیازی به گفتن نیست، برنامه نویسی گام تصادفی می تواند خیلی طول بکشد.

شما باید زمانی برای فک کردن بگذارید. اشکال زدایی مانند یک علم آزمایشگاهی است. شما باید حداقل یک فرضیه درباره اینکه "مشکل چه چیزی است؟" داشته باشید. اگر دو یا چند احتمال وجود داشته باشد سعی کنید به یک آزمون که می تواند یکی از آنها را حذف کند فک کنید.

بهرحال حتی بهترین تکنیک های اشکال زدایی هم شکست خواهند خورند اگر خطاهای زیادی وجود داشته باشد یا اگر کدی که سعی می کنید تصحیح کنید خیلی بزرگ و پیچیده باشد. گاهی بهترین انتخاب برای عقب نشینی ساده کردن برنامه است تا زمانی که به چیزهایی برسید که کار می کنند و شما آنرا درک کنید.

برنامه نویس های تازه کار اغلب برای عقب نشینی بی میل هستند چون آنها نمی توانند یک خط کد را بتنهایی پاک کنند (حتی اگر آن اشتباه باشد). اگر اینکار حال تان را بهتری می کند، برنامه تان را قبل از تکه تکه کردن در یک فایل دیگر کپی کنید سپس می توانید قطعات را یکی یکی کپی کنید.

پیدا کردن اشکال (bug) سخت نیازمند خواندن، اجرا کردن، اندیشه کردن و گاهی عقب نشینی است. اگر روی این فعالیت ها گیر کردید، سعی کنید بقیه را انجام دهید.

#### 13.11 واژه نامه

قطعی: مربوط به یک برنامه که هر وقت آنرا را اجرا کنید با توجه به ورودی های مشابه، یک کار مشابه انجام می دهد.

شبه تصادفی: مربوط به سلسله اعدادی است که بصورت تصادفی ظاهر می شود اما توسط یک برنامه قطعی تولید شده است.

مقدار پیش فرض: مقدار گرفته شده برای یک پارامتر اختیاری، درصورتی که توسط آرگومان مقدار دهی نشده باشد.

باطل کردن: برای جایگزین کردن یک مقدار پیش فرض با یک آرگومان.

محک زنی: فرایند انتخاب بین ساختمان های داده توسط پیاده سازی و تست آنها روی یک نمونه از ورودی های ممکن.

اشکال زدایی به سبک اردک پلاستیکی: اشکال زدایی توسط توضیح دادن به یک شی بیجان مثل یک اردک پلاستیکی. شمرده و مفصل گفتن مشکل می تواند به شما کمک کند آنرا حل کنید؛ حتی اگر اردک پلاستیکی پایتون بلد نباشد!

#### 13.2 تمرین ها
#### تمرین 13.9

"رتبه" یک کلمه، یک موقعیت در لیستی از کلمات مرتب شده بر اساس فراوانی است، بیشترین کلمه رایج رتبه 1 را دارد، دومین کلمه پر تکرار رتبه 2 و الی آخر.

قانون zipf رابطه بین رتبه و فراوانی کلمات در زبان طبیعی را توضیح می دهد (http://en.wikipedia.org/wiki/Zipf's_law). بطور خاص پیش بینی می کند که فراوانی (f) کلمه با رتبه r برابر است با:



بطوریکه s و c پارامترهایی هستند که به زبان و متن بستگی دارند. اگر لگاریتم هر دو طرف تساوی را بگیرید، می شود:



بنابراین اگر نمودار log f در مقابل log r را رسم کنید باید یک خط راست با شیب –s و محل تقاطع log c دریافت کنید.

یک برنامه بنویسید که یک متن را از فایل بخواند، فراوانی کلمات را بشمارد و هر کلمه را با مرتب سازی نزولی نسبت به تکرار بهمراه log f و log r در یک خط چاپ کند. از برنامه نموداری به انتخاب خودتان برای رسم نتیجه استفاده کنید و چک کنید که آیا آنها یک خط راست را تشکیل می دهند یا نه. می توانید مقدار s را تخمین بزنید؟

راه حل: http://thinkpython2.com/code/zipf.py اگر مجموعه anaconda را نصب کرده اید، از قبل کتابخانه matplotlib را دارید؛ در غیر اینصورت باید آنرا نصب کنید.